package di.scanner.analyzer;

import di.scanner.model.JavaClassElement;
import di.scanner.mongo.dao.ModelResultDao;
import di.scanner.mongo.dao.impl.ModelResultDaoImpl;
import di.scanner.mongo.model.ClassField;
import di.scanner.mongo.model.Column;
import di.scanner.mongo.model.JoinColumn;
import di.scanner.mongo.model.ModelResult;
import di.scanner.store.JavaClassStore;
import org.eclipse.jdt.core.dom.*;

import java.lang.reflect.Member;
import java.util.ArrayList;
import java.util.List;

public class ModelAnalyzer {

    private ModelResultDao modelResultDao;

    public ModelAnalyzer(){
        modelResultDao = new ModelResultDaoImpl();
    }

    public void analysis() {
        String domainName = "";
        JavaClassElement element = null;
        while((element = JavaClassStore.getInstance().poll())!=null){
            domainName = element.getDomainName();
            analysisme(element);
        }
        System.out.println(domainName+" analysis done");
    }


    public void analysisme(JavaClassElement javaClassElement){

        TypeDeclaration typeDeclaration = javaClassElement.getTypeDeclaration();
        List<Modifier> modifierList = typeDeclaration.modifiers();
        NormalAnnotation tableAnnotation = getAnnotation(modifierList,ModelResult.TABLE);
        NormalAnnotation entityAnnotion = getAnnotation(modifierList,ModelResult.ENTITY);
        if (tableAnnotation!=null&&entityAnnotion!=null){
            SingleMemberAnnotation accessAnnotation = getSingleMemberAnnotation(modifierList,ModelResult.ACCESS);
            ModelResult modelResult = new ModelResult();
            modelResult.setName(javaClassElement.getClassName());
            modelResult.setEntity(getAnnotationStringValueByName(entityAnnotion,"name"));
            modelResult.setTable(getAnnotationStringValueByName(tableAnnotation,"name"));
            modelResult.setAccess(accessAnnotation.getValue().toString().split("\\.")[1]);
            modelResult.setDomain(javaClassElement.getDomainName());
            List<ClassField> classFieldList = new ArrayList<>();
            List<FieldDeclaration> fieldDeclarationList = javaClassElement.getFieldDeclarationList();
            for (FieldDeclaration fieldDeclaration:fieldDeclarationList){
                ClassField classField = getClassField(fieldDeclaration);
                if (classField!=null){
                    classFieldList.add(classField);
                }
            }
            modelResult.setClassFieldList(classFieldList);

            modelResultDao.save(modelResult);
        }

    }

    public NormalAnnotation getAnnotation(List<Modifier> modifierList,String annotationName){
        NormalAnnotation result = null;
        for (Object modifier:modifierList) {
            if (modifier instanceof NormalAnnotation) {
                NormalAnnotation normalAnnotation = (NormalAnnotation) modifier;
                String typeName = normalAnnotation.getTypeName().getFullyQualifiedName();
                if (annotationName.equals(typeName)){
                    return normalAnnotation;
                }
            }
        }
        return result;
    }

    private NormalAnnotation getTableAnaotation(List<Modifier> modifierList){
        return getAnnotation(modifierList,ModelResult.TABLE);
    }
    private NormalAnnotation getEntityAnaotation(List<Modifier> modifierList){
       return getAnnotation(modifierList,ModelResult.ENTITY);
    }

    private SingleMemberAnnotation getSingleMemberAnnotation(List<Modifier> modifierList,String annotationName){
        SingleMemberAnnotation result = null;
        for (Object modifier:modifierList) {
            if (modifier instanceof SingleMemberAnnotation) {
                SingleMemberAnnotation singleMemberAnnotation = (SingleMemberAnnotation) modifier;
                String typeName = singleMemberAnnotation.getTypeName().getFullyQualifiedName();
                if (annotationName.equals(typeName)){
                    return singleMemberAnnotation;
                }
            }
        }
        return result;
    }

    private Expression getAnnotationValueByName(NormalAnnotation normalAnnotation,String name){
        Expression value = null;
        List values = normalAnnotation.values();
        for (Object valuePair: values) {
            if (valuePair instanceof MemberValuePair){
                MemberValuePair memberValuePair = (MemberValuePair) valuePair;
                if (name.equals(memberValuePair.getName().toString())){
                    value = memberValuePair.getValue();
                }
            }

        }
        return value;
    }

    private String getAnnotationStringValueByName(NormalAnnotation normalAnnotation,String name){
        Expression expression = getAnnotationValueByName(normalAnnotation,name);
        if (expression!=null){
            return expression.toString().replace("\"","");
        }

        return "";
    }

    private String getJoinColumnReference(List<Modifier> modifierList){
        NormalAnnotation reference = getAnnotation(modifierList,ClassField.ONE_TO_ONE);
        if (reference!=null){
            return ClassField.ONE_TO_ONE;
        }
        reference = getAnnotation(modifierList,ClassField.ONE_TO_MANY);
        if (reference!=null){
            return ClassField.ONE_TO_MANY;
        }
        reference = getAnnotation(modifierList,ClassField.MANAY_TO_MANY);
        if (reference!=null){
            return ClassField.MANAY_TO_MANY;
        }
        reference = getAnnotation(modifierList,ClassField.MANY_TO_ONE);
        if (reference!=null){
            return  ClassField.MANY_TO_ONE;
        }
        return null;
    }

    private ClassField getClassField(FieldDeclaration fieldDeclaration){
        List<Modifier> modifierList = fieldDeclaration.modifiers();
        boolean isStatic = false;
        boolean isFinal = false;
        for (Object obj:modifierList){
            if (obj instanceof Modifier){
                Modifier modifier = (Modifier) obj;
                if(modifier.getKeyword().toString().equals("final")){
                    isFinal=true;
                }
                if (modifier.getKeyword().toString().equals("static")){
                    isStatic=true;
                }
            }

        }
       if (isFinal&&isStatic){
            return null;
       }
        ClassField classField = new ClassField();
        String name = "";
        for (Object obj : fieldDeclaration.fragments()) {
            VariableDeclarationFragment frag = (VariableDeclarationFragment) obj;
            name = frag.getName().getFullyQualifiedName();
        }
        classField.setName(name);
        Type type = fieldDeclaration.getType();
        if (type instanceof ParameterizedType){
            ParameterizedType parameterizedType = (ParameterizedType) type;
            Type argumentType = (Type) parameterizedType.typeArguments().get(0);
            String ReferencedModel = null;
            if (isPrimitive(argumentType.toString())){
                ReferencedModel = argumentType.toString();
            }
            classField.setReferencedModel(ReferencedModel);
        }
        classField.setType(fieldDeclaration.getType().toString());
        NormalAnnotation columnAnnotation = getAnnotation(modifierList,ClassField.COLUMN);
        if (columnAnnotation!=null){
            Column column = new Column();
            column.setName(getAnnotationStringValueByName(columnAnnotation,"name"));
            if (getAnnotationValueByName(columnAnnotation,"nullable")!=null)
                column.setNullable(getAnnotationStringValueByName(columnAnnotation,"nullable"));
            classField.setColumn(column);
        }
        NormalAnnotation joinTableAnnotation = getAnnotation(modifierList,ClassField.JOIN_TABLE);
        if (joinTableAnnotation!=null){
            Expression joinColumnsExpress = getAnnotationValueByName(joinTableAnnotation,"joinColumns");
            if (joinColumnsExpress instanceof NormalAnnotation){
                NormalAnnotation joinColumnAnnotation = (NormalAnnotation) joinColumnsExpress;
                JoinColumn joinColumn = getJoinColumn(modifierList, joinColumnAnnotation);
                classField.setJoinColumn(joinColumn);
            }
        }
        NormalAnnotation joinColumnAnnotation = getAnnotation(modifierList,"JoinColumn");
        if (joinColumnAnnotation!=null){
            JoinColumn joinColumn = getJoinColumn(modifierList,joinColumnAnnotation);
            classField.setJoinColumn(joinColumn);
        }

        return classField;
    }

    private JoinColumn getJoinColumn(List<Modifier> modifierList, NormalAnnotation joinColumnAnnotation) {
        JoinColumn joinColumn = new JoinColumn();
        joinColumn.setName(getAnnotationStringValueByName(joinColumnAnnotation,"name"));
        if (getAnnotationValueByName(joinColumnAnnotation,"nullable")!=null)
            joinColumn.setNullable(getAnnotationStringValueByName(joinColumnAnnotation,"nullable"));
        joinColumn.setReference(getJoinColumnReference(modifierList));
        if (getAnnotationValueByName(joinColumnAnnotation,"referencedColumnName")!=null)
            joinColumn.setReferencedColumnName(getAnnotationStringValueByName(joinColumnAnnotation,"referencedColumnName"));
        return joinColumn;
    }

    private boolean isPrimitive(String clazz){
        List<String> primitiveList = new ArrayList<>();
        primitiveList.add("int");
        primitiveList.add("Integer");
        primitiveList.add("byte");
        primitiveList.add("Byte");
        primitiveList.add("short");
        primitiveList.add("Short");
        primitiveList.add("long");
        primitiveList.add("Long");
        primitiveList.add("float");
        primitiveList.add("Float");
        primitiveList.add("double");
        primitiveList.add("Double");
        primitiveList.add("boolean");
        primitiveList.add("Boolean");
        primitiveList.add("char");
        primitiveList.add("Char");
        primitiveList.add("String");
        if (primitiveList.contains(clazz)){
            return false;
        }
        return true;
    }


}
